#include <string.h>
#include "..\System.h"
#include "Zalloc.h"

///<summary>
/// System_Zalloc
///</summary>
#define	System_Zalloc_MAGIC 0x1d1d
typedef struct _System_ZallocRecord {
	struct _System_ZallocRecord *Prev;
	struct _System_ZallocRecord *Next;
	short Magic;
	//+ for group free
	short Tag;
	int_ Length;
} System_ZallocRecord;
//+ fields
private System_ZallocRecord space(System_Zalloc,_head);
private int_ space(System_Zalloc,_count);
private int_ space(System_Zalloc,_length);

///<summary>
/// cctor
///</summary>
public static_ void space(System_Zalloc,_cctor)() {
	space(System_Zalloc,_head).Next = space(System_Zalloc,_head).Prev = &space(System_Zalloc,_head);
}

///<summary>
/// zalloc
///</summary>
keyword_ void *zalloc(int_ length) {
	return zalloc2(length, 0);
}
///<summary>
/// zalloc2
///</summary>
keyword_ void *zalloc2(int_ length, short tag) {
	System_ZallocRecord *zone;
	length += sizeof(System_ZallocRecord);
	zone = (System_ZallocRecord *)malloc(length);
	if (zone == null) {
		throw(FATAL, L"zalloc: failed on allocation of %i bytes", length);
	}
	memset(zone, 0, length);
	space(System_Zalloc,_count)++;
	space(System_Zalloc,_length) += length;
	zone->Magic = System_Zalloc_MAGIC;
	zone->Tag = tag;
	zone->Length = length;
	zone->Next = space(System_Zalloc,_head).Next;
	zone->Prev = &space(System_Zalloc,_head);
	space(System_Zalloc,_head).Next->Prev = zone;
	space(System_Zalloc,_head).Next = zone;
	return (void *)(zone + 1);
}

///<summary>
/// zfree
///</summary>
keyword_ void zfree(void *obj) {
	System_ZallocRecord *zone = ((System_ZallocRecord *)obj) - 1;
	if (zone->Magic != System_Zalloc_MAGIC) {
		throw(FATAL, L"Zone_Free: bad magic");
	}
	zone->Prev->Next = zone->Next;
	zone->Next->Prev = zone->Prev;
	space(System_Zalloc,_count)--;
	space(System_Zalloc,_length) -= zone->Length;
	free(zone);
}

///<summary>
/// zfreeTag
///</summary>
keyword_ void zfreeTag(int_ tag) {
	System_ZallocRecord *zoneNext;
	System_ZallocRecord *zone;
	for (zone = space(System_Zalloc,_head).Next; zone != &space(System_Zalloc,_head); zone = zoneNext) {
		zoneNext = zone->Next;
		if (zone->Tag == tag) {
			zfree((void *)(zone + 1));
		}
	}
}

///<summary>
/// CopyText
///</summary>
public static_ char *space(System_Zalloc,CopyText)(char *text) {
	int_ newTextLength = strlen(text) + 1;
	char *newText = (char *)zalloc(newTextLength);
	strcpy_s(newText, newTextLength, text);
	return newText;
}

///<summary>
/// GetStats
///</summary>
public static_ void space(System_Zalloc,GetStats)(out int_ *length, out int_ *count) {
	*length = space(System_Zalloc,_length);
	*count = space(System_Zalloc,_count);
}
